use std::{
  net::{Ipv6Addr, SocketAddr},
  sync::Arc,
};

use aok::Void;
use axum::extract::Request;
use hyper::{Method, StatusCode, body::Incoming};
use hyper_util::{
  rt::{TokioExecutor, TokioIo},
  server,
};
use tokio::{net::TcpListener, sync::Semaphore};

use crate::{proxy::proxy, resp};
// use tower::{Service, ServiceExt};

genv::def!(IPV6_PROXY_USER, IPV6_PROXY_PASSWD);

pub const MAX_CONCURRENT: usize = 1024;

pub async fn srv(bind: SocketAddr, (ipv6, prefix_len): (Ipv6Addr, u8)) -> Void {
  let listener = TcpListener::bind(bind).await?;

  let ipv6: u128 = ipv6.into();

  let semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT));

  loop {
    let (socket, _remote_addr) = listener.accept().await?;

    let semaphore = semaphore.clone();

    tokio::spawn(async move {
      let socket = TokioIo::new(socket);

      let hyper_service = hyper::service::service_fn(|req: Request<Incoming>| {
        let semaphore = semaphore.clone();
        async move {
          let method = req.method().clone();
          let uri = req.uri();
          let host = uri.host().unwrap_or_default().to_owned();
          let path = uri
            .path_and_query()
            .map(|i| i.to_string())
            .unwrap_or_default();
          if method != Method::CONNECT {
            return resp(StatusCode::METHOD_NOT_ALLOWED);
          }
          let _lock = semaphore.acquire().await;
          match proxy(
            ipv6,
            prefix_len,
            &[
              IPV6_PROXY_USER::<String>().as_bytes(),
              b":",
              IPV6_PROXY_PASSWD::<String>().as_bytes(),
            ]
            .concat(),
            req,
          )
          .await
          {
            Ok(r) => Ok(r),
            Err(err) => {
              log::warn!("{method} {host}{path}: {}", err);
              Err(err)
            }
          }
        }
      });

      let _ = server::conn::auto::Builder::new(TokioExecutor::new())
        .serve_connection_with_upgrades(socket, hyper_service)
        .await;
    });
  }
}
